package com.mp.approvalreq.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SimplePropertyPreFilter;
import com.managerplus.key.service.KeyGeneratorServicePrx;
import com.mp.approvalreq.dao.*;
import com.mp.approvalreq.dao.impl.*;
import com.mp.approvalreq.entity.*;
import com.mp.approvalreq.entity.dto.ComponentGroupDTO;
import com.mp.approvalreq.entity.dto.ExchangeTemplateDTO;
import com.mp.approvalreq.entity.dto.GeneralConditionFlowDTO;
import com.mp.approvalreq.entity.dto.GeneralRequestDTO;
import com.mp.approvalreq.service.ApprovalOperateServiceServant;
import com.mp.approvalreq.util.*;
import com.mp.auth.entity.CommonSetting;
import com.mp.auth.service.CommonSettingServicePrx;
import com.mp.checkin.entity.LeaveInfo;
import com.mp.checkin.service.CheckInDataServicePrx;
import com.mp.user.entity.User;
import com.mp.user.service.UserServicePrx;
import com.mp.user.util.UserUtils;
import com.plusmoney.exception.RpcServerLogicalException;
import com.plusmoney.exception.enums.ApprovalRespCodeDesc;
import com.plusmoney.jdbc.util.DBUtils;
import com.plusmoney.util.DateTimeHelper;
import com.plusmoney.util.Utils;
import com.qq.tars.client.util.ServantUtils;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.*;
import java.util.stream.Collectors;

public class ApprovalOperateServantImpl implements ApprovalOperateServiceServant {

    private static final Logger logger = LoggerFactory.getLogger(ApprovalOperateServantImpl.class);

    @Override
    public void confirmRequest(Integer requestId, User user) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);

        try {
            CcDao ccDao = DaoFactory.getCcDao(connection);
            FlowDao flowDao = new FlowDaoImpl(connection);
            RequestDao requestDao = new RequestDaoImpl(connection);
            MoneyDao moneyDao = new MoneyDaoImpl(connection);

            Request request = requestDao.selectRequestDetailById(requestId);
            List<Flow> flows = flowDao.selectFlowsByRequestId(requestId);

            checkUpdateFlow(request);

            List<Flow> currentFlows = FlowUtils.getCurrentFlows(flows);
            Flow matchFlow = FlowUtils.getFlowsByCompanyInfoId(currentFlows, user.getCompanyInfoId());
            if (Utils.isNull(matchFlow)) {
                if (FlowUtils.isExistUserFlow(flows, user)) {
                    logger.info("重复批准,直接返回");
                    return;
                }
                throw new RpcServerLogicalException(ApprovalRespCodeDesc.ILLEGAL_OPERATION);
            }

            List<Flow> flowsToUpdate = new ArrayList<>();
            // 处理当前审批流
            matchFlow.setIsApprovedAt(DateTimeHelper.format(new Date(), DateTimeHelper.DEFAULT_PATTERN));
            matchFlow.setIsApproved(Flow.CONFIRM);
            flowsToUpdate.add(matchFlow);
            if (matchFlow.getType() == Flow.MODE_OR) {
                // 或签
                for (Flow currentFlow : currentFlows) {
                    if (currentFlow == matchFlow) {
                        continue;
                    }
                    currentFlow.setIsApproved(Flow.SKIP);
                    flowsToUpdate.add(currentFlow);
                }
            }
            // 组装审批信息（兼容旧逻辑）
            request.setCheckedTime(FlowUtils.getApproveTimes(flows));
            request.setWhoConfirm(FlowUtils.getConfirmContactIds(flows));
            request.setWhoConfirmCiid(FlowUtils.getConfirmCompanyInfoIds(flows));
            request.setLatestApprovedTime(DateTimeHelper.format(new Date(), DateTimeHelper.DEFAULT_PATTERN));
            // 下一层审批流
            List<Flow> nextFlows = FlowUtils.getFlowsByOrdering(flows, matchFlow.getOrdering() + 1);
            if (FlowUtils.isConfirmByOrdering(currentFlows)) {
                // 当前层级已经批准（考虑会签和或签的情况）
                request.addRequestStep();
                // 批准后跳到下一层审批流
                if (Utils.isEmpty(nextFlows)) {
                    logger.info("已全部批准");
                    request.setFinished(Finished.AGREED);
                    if (currentFlows.get(0).getFlowType() == Flow.FLOW_TYPE_FINANCIAL) {
                        // 已付款
                        request.setFinancialStatus(2);
                    }
                    request.setFinallyConfirmedTime(DateTimeHelper.format(new Date(), DateTimeHelper.DEFAULT_PATTERN));
                    request.setWhoTheNext(null);
                    request.setWhoTheNextCiid(null);
                } else {
                    logger.info("下一层审批流:{}", JSONObject.toJSONString(nextFlows));
                    for (Flow flow : nextFlows) {
                        flow.setIsApproved(Flow.APPROVING);
                        flowsToUpdate.add(flow);
                    }
                    Flow nextFlow = nextFlows.get(0);
                    request.setWhoTheNext(String.valueOf(nextFlow.getContactId()));
                    request.setWhoTheNextCiid(String.valueOf(nextFlow.getCompanyInfoId()));
                    if (nextFlow.getFlowType() == Flow.FLOW_TYPE_FINANCIAL) {
                        logger.info("普通审批流已全部批准");
                        request.setFinished(Finished.AGREED);
                        List<Flow> financialFlows = FlowUtils.getFinancialFlows(flows);
                        Flow lastFinancialFlow = Utils.getLast(financialFlows);
                        if (Utils.isNotNull(lastFinancialFlow) && lastFinancialFlow.getOrdering() == nextFlow.getOrdering()) {
                            logger.info("下一级是最后一级财务审批人");
                            request.setFinancialStatus(1);
                        }
                    }
                }
            } else {
                // 会签不完全批准,兼容就逻辑（2020年8月20日11:46:59）
                getAnotherNext(user, request, currentFlows);
            }

            // 初始化组件信息（2020年8月20日11:47:36）
            initSuite(request, connection);

            DBUtils.begin(connection);
            flowDao.updateBatchFlow(flowsToUpdate);
            requestDao.updateRequestInfo(request);
            if (request.getFinished() == Finished.AGREED && Utils.isEmpty(nextFlows)) {
                // 将抄送记录置为 待归档
                ccDao.letCcCouldFile(requestId, user.getCompanyId());
            }
            if (request.getFinished() == Finished.AGREED && request.getDefaultType() == Template.OTHER & Utils.isEmpty(nextFlows)) {
                // 修改金额表（2020年8月20日11:51:01）
                moneyDao.updateMoneyState(request.getRequestDkey(), 1);
            }
            DBUtils.commit(connection);

            RocketUtils.sendConfirmMsg(user, request, nextFlows);

        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            DBUtils.rollback(connection);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }

    private void initSuite(Request request, Connection connection) {
        if (request.getFinished() != Finished.AGREED) {
            return;
        }
        try {
            switch (request.getDefaultType()) {
                case Template.LEAVE:
                case Template.COMPENSATORY_LEAVE:
                case Template.DAYS_OFF:
                    LeaveDao leaveDao = new LeaveDaoImpl(connection);
                    request.setLeave(leaveDao.selectLastLeaveByRequestId(request.getRequestDkey()));
                    return;
                case Template.OUTDOOR:
                case Template.BUSINESS_TRIP:
                    OutdoorDao outdoorDao = new OutdoorDaoImpl(connection);
                    request.setOutdoor(outdoorDao.selectLastOutdoorByRequestId(request.getRequestDkey()));
                    return;
                case Template.APPEND_SIGN:
                    AppendSignDao appendSignDao = new AppendSignDaoImpl(connection);
                    request.setAppendSigns(appendSignDao.selectAppendSignsByRequestId(request.getRequestDkey()));
                    return;
                case Template.WORK_OVERTIME:
                    OvertimeDao overtimeDao = new OvertimeDaoImpl(connection);
                    request.setOvertimes(overtimeDao.selectOvertimesByRequestId(request.getRequestDkey()));
                    return;
                default:
                    break;
            }
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
        }

    }

    @Override
    public void refuseRequest(Integer requestId, User user) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            FlowDao flowDao = new FlowDaoImpl(connection);
            RequestDao requestDao = new RequestDaoImpl(connection);

            Request request = requestDao.selectRequestDetailById(requestId);
            List<Flow> flows = flowDao.selectFlowsByRequestId(requestId);

            checkUpdateFlow(request);

            List<Flow> currentFlows = FlowUtils.getCurrentFlows(flows);
            Flow matchFlow = FlowUtils.getFlowsByCompanyInfoId(currentFlows, user.getCompanyInfoId());
            if (Utils.isNull(matchFlow)) {
                if (FlowUtils.isExistUserFlow(flows, user)) {
                    logger.info("重复操作,直接返回");
                    return;
                }
                throw new RpcServerLogicalException(ApprovalRespCodeDesc.ILLEGAL_OPERATION);
            }

            List<Flow> flowsToUpdate = new ArrayList<>();
            matchFlow.setIsApprovedAt(DateTimeHelper.format(new Date(), DateTimeHelper.DEFAULT_PATTERN));
            matchFlow.setIsApproved(Flow.REFUSED);
            flowsToUpdate.add(matchFlow);
            if (matchFlow.getType() == Flow.MODE_AND) {
                for (Flow currentFlow : currentFlows) {
                    if (currentFlow == matchFlow) {
                        continue;
                    }
                    currentFlow.setIsApproved(Flow.SKIP);
                    flowsToUpdate.add(currentFlow);
                }
            }
            if (FlowUtils.isRefuseByOrdering(currentFlows)) {
                request.addRequestStep();
                request.setFinished(Finished.REFUSED);
                request.setFinancialStatus(0);
                request.setWhoTheNext(null);
                request.setWhoTheNextCiid(null);
            } else {
                getAnotherNext(user, request, currentFlows);
            }

            request.setCheckedTime(FlowUtils.getApproveTimes(flows));
            request.setWhoRefused(FlowUtils.getRefuseContactIds(flows));
            request.setWhoRefusedCiid(FlowUtils.getRefuseCompanyInfoIds(flows));
            request.setLatestApprovedTime(DateTimeHelper.format(new Date(), DateTimeHelper.DEFAULT_PATTERN));

            DBUtils.begin(connection);
            flowDao.updateBatchFlow(flowsToUpdate);
            requestDao.updateRequestInfo(request);
            if (request.getFinished() == Finished.REFUSED) {
                logger.info("获取并作废套件信息");
                initAndDisableSuite(request, user, connection, request.getDefaultType());
            }

            DBUtils.commit(connection);

            RocketUtils.sendRefuseMsg(user, request);
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            DBUtils.rollback(connection);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }

    }

    private void getAnotherNext(User user, Request request, List<Flow> currentFlows) {
        for (Flow currentFlow : currentFlows) {
            if (!Objects.equals(currentFlow.getCompanyInfoId(), user.getCompanyInfoId())) {
                request.setWhoTheNext(String.valueOf(currentFlow.getContactId()));
                request.setWhoTheNextCiid(String.valueOf(currentFlow.getCompanyInfoId()));
            }
        }
    }

    private void checkUpdateFlow(Request request) {
        if (Utils.isNull(request)) {
            throw new RpcServerLogicalException(ApprovalRespCodeDesc.REQUEST_MISSING);
        }
        if (request.getFinished() == Finished.CALLING_BACK) {
            throw new RpcServerLogicalException(ApprovalRespCodeDesc.AFTER_CALLBACK_OPERATION);
        }
    }

    @Override
    public void fileRequest(Request dto) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            CcDao ccDao = DaoFactory.getCcDao(connection);
            RequestDao requestDao = new RequestDaoImpl(connection);

            User user = dto.getUser();
            Request request = requestDao.selectRequestDetailById(dto.getRequestDkey());

            List<Cc> ccList = ccDao.selectCcListByRequestId(dto.getRequestDkey());
            Cc cc = CcUtils.getCcByCompanyInfoId(ccList, user.getCompanyInfoId());

            if (Utils.isNull(cc) || cc.getFiledStatus() == Cc.DEFAULT) {
                throw new RpcServerLogicalException(ApprovalRespCodeDesc.WRONG_OPERATION);
            }
            if (cc.getFiledStatus() == Cc.HAD_FILED) {
                logger.info("用户{}已归档审批{}", cc.getCcCiid(), dto.getRequestDkey());
                return;
            }
            cc.setFiledStatus(Cc.HAD_FILED);
            String fileTime = DateTimeHelper.format(new Date(), DateTimeHelper.DEFAULT_PATTERN);
            cc.setCcTime(fileTime);
            request.setFinallyCcTime(fileTime);

            if (CcUtils.isAllFiled(ccList)) {
                request.setFinished(Finished.ARCHIVED);
                request.setFinancialStatus(0);
            }

            DBUtils.begin(connection);
            ccDao.updateCcFiledStatus(cc);
            requestDao.updateRequestCcInfo(request);
            DBUtils.commit(connection);

            RocketUtils.sendFileMsg(user, request);

        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            DBUtils.rollback(connection);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }

    @Override
    public void fileAllRequest(User user) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            CcDao ccDao = DaoFactory.getCcDao(connection);
            RequestDao requestDao = new RequestDaoImpl(connection);

            List<Integer> requestIds = ccDao.selectNeedToFileRequestIds(user.getCompanyId(), user.getCompanyInfoId());
            if (Utils.isEmpty(requestIds)) {
                return;
            }
            List<Request> requests = requestDao.selectRequestByIds(Utils.list2string(requestIds));
            Map<Integer, List<Cc>> ccMap = ccDao.selectBatchCc(Utils.list2string(requestIds));
            String fileTime = DateTimeHelper.format(new Date(), DateTimeHelper.DEFAULT_PATTERN);
            List<Cc> ccToUpdate = new ArrayList<>();
            List<Request> requestToUpdate = new ArrayList<>();
            for (Request request : requests) {
                List<Cc> ccList = ccMap.get(request.getRequestDkey());
                Cc cc = CcUtils.getCcByCompanyInfoId(ccList, user.getCompanyInfoId());
                if (Utils.isNull(cc) || cc.getFiledStatus() != Cc.NEED_FILE) {
                    continue;
                }
                cc.setFiledStatus(Cc.HAD_FILED);
                cc.setCcTime(fileTime);
                ccToUpdate.add(cc);
                request.setFinallyCcTime(fileTime);
                if (CcUtils.isAllFiled(ccList)) {
                    request.setFinished(Finished.ARCHIVED);
                    request.setFinancialStatus(0);
                    requestToUpdate.add(request);
                }
            }
            logger.info("{},待更新抄送{}条,待更新审批:{}条", fileTime, ccToUpdate.size(), requestToUpdate.size());
            DBUtils.begin(connection);
            ccDao.updateBatchCcFiledStatus(ccToUpdate);
            if (Utils.isNotEmpty(requestToUpdate)) {
                requestDao.updateRequestCcBatch(requestToUpdate);
            }
            DBUtils.commit(connection);

            RocketUtils.pushToCompanyRefresh(user.getCompanyId());
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            DBUtils.rollback(connection);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }

    @Override
    public void archiveAllRequest(Request dto) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            CcDao ccDao = DaoFactory.getCcDao(connection);
            RequestDao requestDao = new RequestDaoImpl(connection);

            List<Integer> requestIds = ccDao.selectNeedToFileRequestIds(dto.getCompanyId(), dto.getCompanyInfoId());
            if (Utils.isEmpty(requestIds)) {
                return;
            }
            if (Utils.isNotEmpty(dto.getRequestIds())) {
                requestIds = ListUtils.intersection(requestIds, dto.getRequestIds());
            }
            List<Request> requests = requestDao.selectRequestByIds(Utils.list2string(requestIds));
            Map<Integer, List<Cc>> ccMap = ccDao.selectBatchCc(Utils.list2string(requestIds));
            String fileTime = DateTimeHelper.format(new Date(), DateTimeHelper.DEFAULT_PATTERN);
            List<Cc> ccToUpdate = new ArrayList<>();
            List<Request> requestToUpdate = new ArrayList<>();
            for (Request request : requests) {
                List<Cc> ccList = ccMap.get(request.getRequestDkey());
                Cc cc = CcUtils.getCcByCompanyInfoId(ccList, dto.getCompanyInfoId());
                if (Utils.isNull(cc) || cc.getFiledStatus() != Cc.NEED_FILE) {
                    continue;
                }
                cc.setFiledStatus(Cc.HAD_FILED);
                cc.setCcTime(fileTime);
                ccToUpdate.add(cc);
                request.setFinallyCcTime(fileTime);
                if (CcUtils.isAllFiled(ccList)) {
                    request.setFinished(Finished.ARCHIVED);
                    request.setFinancialStatus(0);
                    requestToUpdate.add(request);
                }
            }
            logger.info("{},待更新抄送{}条,待更新审批:{}条", fileTime, ccToUpdate.size(), requestToUpdate.size());
            DBUtils.begin(connection);
            ccDao.updateBatchCcFiledStatus(ccToUpdate);
            if (Utils.isNotEmpty(requestToUpdate)) {
                requestDao.updateRequestCcBatch(requestToUpdate);
            }
            DBUtils.commit(connection);

            RocketUtils.pushToCompanyRefresh(dto.getCompanyId());

        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            DBUtils.rollback(connection);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }

    @Override
    public void withdrawFiled(Request dto) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            CcDao ccDao = DaoFactory.getCcDao(connection);
            RequestDao requestDao = new RequestDaoImpl(connection);

            User user = dto.getUser();
            Request request = requestDao.selectRequestDetailById(dto.getRequestDkey());

            List<Cc> ccList = ccDao.selectCcListByRequestId(dto.getRequestDkey());
            Cc cc = CcUtils.getCcByCompanyInfoId(ccList, user.getCompanyInfoId());

            if (Utils.isNull(cc)) {
                throw new RpcServerLogicalException(ApprovalRespCodeDesc.WRONG_OPERATION);
            }
            if (cc.getFiledStatus() != Cc.HAD_FILED) {
                logger.info("用户{}未归档审批{}", cc.getCcCiid(), dto.getRequestDkey());
                return;
            }
            cc.setFiledStatus(Cc.NEED_FILE);
            cc.setCcTime(null);
            if (request.getFinished() == Finished.ARCHIVED) {
                request.setFinallyCcTime(null);
                request.setFinished(Finished.AGREED);
            }

            DBUtils.begin(connection);
            requestDao.updateRequestCcInfo(request);
            ccDao.updateCcFiledStatus(cc);
            DBUtils.commit(connection);

            RocketUtils.sendWithdrawFiledMsg(user, request);
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            DBUtils.rollback(connection);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }


    @Override
    public void cancelRequest(Request dto) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            RequestDao requestDao = new RequestDaoImpl(connection);
            CcDao ccDao = new CcDaoImpl(connection);
            FlowDao flowDao = new FlowDaoImpl(connection);

            User user = dto.getUser();
            Request request = requestDao.selectRequestDetailById(dto.getRequestDkey());

            checkCancelRequest(user, request, flowDao);

            request.setFinished(Finished.CANCELLED);
            request.setOperatorId(user.getCompanyInfoId());
            request.setDisableTime(DateTimeHelper.format(new Date(), DateTimeHelper.DEFAULT_PATTERN));
            request.setLatestApprovedTime(DateTimeHelper.format(new Date(), DateTimeHelper.DEFAULT_PATTERN));
            // 修改财务审批状态（2020年8月20日14:06:26）
            request.setFinancialStatus(0);

            DBUtils.begin(connection);

            logger.info("作废审批");
            requestDao.updateRequestOperation(request);
            logger.info("作废抄送记录");
            ccDao.deleteCcByRequestId(dto.getRequestDkey(), user.getCompanyId());
            logger.info("获取并作废套件信息");
            initAndDisableSuite(request, user, connection, request.getDefaultType());
            logger.info("修改审批流状态");
            disableFlow(dto.getRequestDkey(), user, connection);
            logger.info("作废参与人");
            disableParticipants(dto.getRequestDkey(), user, connection);

            DBUtils.commit(connection);

            RocketUtils.sendCancelMsg(user, request);

        } catch (Throwable e) {
            DBUtils.rollback(connection);
            logger.error(e.getMessage(), e);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }

    private void checkCancelRequest(User user, Request request, FlowDao flowDao) throws Throwable {
        if (Utils.isNull(request)) {
            throw new RpcServerLogicalException(ApprovalRespCodeDesc.REQUEST_MISSING);
        }
        if (request.getFinished() != Finished.AGREED) {
            throw new RpcServerLogicalException(1, "当前审批状态无法作废");
        }
        if (UserUtils.isAdmin(user)) {
            // 老板直接可以作废
            return;
        }
        List<Flow> flows = flowDao.selectFlowsByRequestId(request.getRequestDkey());
        request.setFlows(flows);
        // 最后一层审批人才可以作废审批
        List<Flow> lastFlows = FlowUtils.getLastFlows(request.getFlows());
        Flow flow = FlowUtils.getFlowsByCompanyInfoId(lastFlows, user.getCompanyInfoId());
        if (Utils.isNotNull(flow) && flow.getIsApproved() == Flow.CONFIRM) {
            return;
        }
        throw new RpcServerLogicalException(ApprovalRespCodeDesc.ILLEGAL_OPERATION);
    }

    @Override
    public void deleteRequest(Request dto) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            CcDao ccDao = DaoFactory.getCcDao(connection);
            RequestDao requestDao = new RequestDaoImpl(connection);

            User user = dto.getUser();
            Request request = requestDao.selectRequestDetailById(dto.getRequestDkey());

            checkDeleteRequest(request, user);
            request.setOperatorId(user.getCompanyInfoId());
            request.setDisableTime(DateTimeHelper.format(new Date(), DateTimeHelper.DEFAULT_PATTERN));
            request.setFinished(Finished.DELETED);

            DBUtils.begin(connection);
            logger.info("删除审批");
            requestDao.updateRequestOperation(request);
            logger.info("作废抄送人");
            ccDao.deleteCcByRequestId(dto.getRequestDkey(), user.getCompanyId());
            logger.info("获取并作废套件信息");
            initAndDisableSuite(request, user, connection, request.getDefaultType());
            logger.info("作废审批流");
            disableFlow(dto.getRequestDkey(), user, connection);
            logger.info("作废参与人");
            disableParticipants(dto.getRequestDkey(), user, connection);

            DBUtils.commit(connection);


            RocketUtils.sendDeleteMsg(user, request);

        } catch (Throwable e) {
            DBUtils.rollback(connection);
            logger.error(e.getMessage(), e);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }

    private void checkDeleteRequest(Request request, User user) {
        if (UserUtils.isAdmin(user)) {
            return;
        }
        if (Utils.isNull(request)) {
            throw new RpcServerLogicalException(ApprovalRespCodeDesc.REQUEST_MISSING);
        }

        if (request.getFinished() != Finished.CALLING_BACK) {
            throw new RpcServerLogicalException(ApprovalRespCodeDesc.WRONG_OPERATION);
        }

        if (!Objects.equals(user.getCompanyInfoId(), request.getApplicantCiid())) {
            throw new RpcServerLogicalException(ApprovalRespCodeDesc.ILLEGAL_OPERATION);
        }
    }

    @Override
    public void withdrawApply(Request dto) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            CcDao ccDao = DaoFactory.getCcDao(connection);
            RequestDao requestDao = new RequestDaoImpl(connection);
            FlowDao flowDao = new FlowDaoImpl(connection);


            User user = dto.getUser();
            Request request = requestDao.selectRequestDetailById(dto.getRequestDkey());

            checkWithdrawApply(request, user, flowDao);

            request.setFinished(Finished.CALLING_BACK);
            request.setRequestStep(Finished.CALLING_BACK);
            request.setCallBackTime(DateTimeHelper.format(new Date(), DateTimeHelper.DEFAULT_PATTERN));
            request.setOperatorId(user.getCompanyInfoId());

            DBUtils.begin(connection);
            requestDao.updateRequestOperation(request);

            ccDao.deleteCcByRequestId(dto.getRequestDkey(), user.getCompanyId());
            logger.info("获取并作废套件信息");
            initAndDisableSuite(request, user, connection, request.getDefaultType());
            logger.info("修改审批流状态(作废审批流)");
            disableFlow(dto.getRequestDkey(), user, connection);
            logger.info("作废参与人");
            disableParticipants(dto.getRequestDkey(), user, connection);

            DBUtils.commit(connection);

            RocketUtils.sendWithdrawApplyMsg(user, request);

        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            DBUtils.rollback(connection);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }

    private void checkWithdrawApply(Request request, User user, FlowDao flowDao) throws Throwable {
        if (Utils.isNull(request)) {
            throw new RpcServerLogicalException(ApprovalRespCodeDesc.REQUEST_MISSING);
        }
        if (!Objects.equals(request.getApplicantCiid(), user.getCompanyInfoId())
                && !Objects.equals(request.getProxyContactId(), user.getContactId())) {
            throw new RpcServerLogicalException(1, "非法操作");
        }
        List<Flow> flows = flowDao.selectFlowsByRequestId(request.getRequestDkey());
        request.setFlows(flows);
        if (FlowUtils.existConfirmOrRefuse(request.getFlows())) {
            throw new RpcServerLogicalException(1, "审批已被处理，无法撤销");
        }
    }

    @Override
    public void withdrawApprove(Request dto) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            CcDao ccDao = DaoFactory.getCcDao(connection);
            FlowDao flowDao = new FlowDaoImpl(connection);
            RequestDao requestDao = new RequestDaoImpl(connection);

            User user = dto.getUser();
            Request request = requestDao.selectRequestDetailById(dto.getRequestDkey());
            if (request.getFinished() != Finished.APPROVING && Objects.equals(request.getFinancialStatus(), 2)) {
                throw new RpcServerLogicalException(1, "当前审批状态无法撤销");
            }

            List<Flow> flows = flowDao.selectFlowsByRequestId(dto.getRequestDkey());
            Flow userFlow = FlowUtils.getLastConfirmByCompanyInfoId(flows, user.getCompanyInfoId());

            List<Flow> flowsToUpdate = new ArrayList<>();

            if (Utils.isNull(userFlow) || !userFlow.hasConfirmed()) {
                throw new RpcServerLogicalException(1, "无权操作");
            }

            List<Flow> nextFlows = FlowUtils.getFlowsByOrdering(flows, userFlow.getOrdering() + 1);
            if (Utils.isNotEmpty(nextFlows)) {
                for (Flow flow : nextFlows) {
                    if (StringUtils.isNotBlank(flow.getIsApprovedAt())) {
                        throw new RpcServerLogicalException(1, "该审批已被他人处理，不可撤销");
                    }
                    if (flow.getIsApproved() == Flow.APPROVING) {
                        flow.setIsApproved(Flow.NOT_YET);
                        flowsToUpdate.add(flow);
                    }
                }
            }

            userFlow.setIsApprovedAt(null);
            userFlow.setIsApproved(Flow.APPROVING);
            flowsToUpdate.add(userFlow);
            logger.info("撤销我的批准:{}", userFlow);

            FlowUtils.transferRequestInfo(request, user, flows);
            // 设置审批步骤
            request.setRequestStep(request.getRequestStep() - 1);

            DBUtils.begin(connection);
            flowDao.updateBatchFlow(flowsToUpdate);
            // 将抄送人的记录 置为 无需归档
            ccDao.letCcCouldNotFile(request.getRequestDkey(), user.getCompanyId());
            requestDao.updateRequestInfo(request);
            DBUtils.commit(connection);

            RocketUtils.sendWithdrawApproveMsg(user, request);

        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            DBUtils.rollback(connection);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }


    @Override
    public void pauseRequest(Request dto) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            RequestDao requestDao = new RequestDaoImpl(connection);
            FlowDao flowDao = new FlowDaoImpl(connection);
            LeaveDao leaveDao = new LeaveDaoImpl(connection);
            OutdoorDao outdoorDao = new OutdoorDaoImpl(connection);
            ComponentDao componentDao = new ComponentImpl(connection);

            User user = dto.getUser();
            Request request = requestDao.selectRequestDetailById(dto.getRequestDkey());
            List<Flow> flows = flowDao.selectFlowsByRequestId(dto.getRequestDkey());

            checkPauseRequest(request, user, flows);

            switch (request.getDefaultType()) {
                case Template.LEAVE:
                case Template.COMPENSATORY_LEAVE:
                case Template.DAYS_OFF:
                    pauseLeave(leaveDao, user, request);
                    break;
                case Template.OUTDOOR:
                case Template.BUSINESS_TRIP:
                    pauseOutdoor(outdoorDao, request);
                    break;
                default:
                    throw new RpcServerLogicalException(1, "审批类型有误");
            }

            DBUtils.begin(connection);
            Outdoor outdoor = request.getOutdoor();
            if (Utils.isNotNull(request.getLeave())) {
                logger.info("更新请假套件信息:{}", request.getLeave());
                leaveDao.deleteLeave(request.getLeave());
                leaveDao.insertPauseLeaveInfo(request.getLeave());
                requestDao.updateRequestPauseLeaveInfo(request);
                if (Objects.equals(request.getDefaultType(), Template.DAYS_OFF)) {
                    componentDao.updateDaysOffComponent(request.getLeave());
                }
            } else if (Utils.isNotNull(outdoor)) {
                logger.info("更新外出套件信息:{}", outdoor);
                outdoor.setDuration(OutdoorUtils.calculateDuration(outdoor));
                outdoorDao.deleteOutdoor(outdoor);
                outdoorDao.insertOutdoor(outdoor);
                int timeNum = ApprovalUtils.getEndTimeNum(request.getDefaultType());
                componentDao.updateOutdoorEndTimeComponent(outdoor, timeNum);
            }
            DBUtils.commit(connection);


            RocketUtils.sendPauseMsg(user, request);

        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            DBUtils.rollback(connection);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }

    /**
     * 提前结束外出/出差
     *
     * @param outdoorDao
     * @param request
     * @throws SQLException
     */
    private void pauseOutdoor(OutdoorDao outdoorDao, Request request) throws SQLException {
        Outdoor outdoor = outdoorDao.selectLastOutdoorByRequestId(request.getRequestDkey());
        Date pauseLeaveTime = new Date();
        if (pauseLeaveTime.after(outdoor.getEnd())) {
            throw new RpcServerLogicalException(1, ApprovalUtils.getTemplateDefaultName(request.getDefaultType()) + "已自动结束");
        }
        outdoor.setEnd(pauseLeaveTime);
        outdoor.setEndTime(DateTimeHelper.format(pauseLeaveTime, DateTimeHelper.DEFAULT_PATTERN));
        request.setOutdoor(outdoor);
    }

    /**
     * 销假
     *
     * @param leaveDao
     * @param user
     * @param request
     * @throws Throwable
     */
    private void pauseLeave(LeaveDao leaveDao, User user, Request request) throws Throwable {
        Leave leave = leaveDao.selectLastLeaveByRequestId(request.getRequestDkey());
        Date pauseLeaveTime = new Date();
        // TODO leave.getLeaveTimeUnit()
        if (leave.getRealEndTime().before(pauseLeaveTime)) {
            throw new RpcServerLogicalException(1, ApprovalUtils.getTemplateDefaultName(request.getDefaultType()) + "已自动结束");
        }
        try {
            CommonSettingServicePrx commonSettingServicePrx = ServantUtils.getObject(CommonSettingServicePrx.class);
            CommonSetting commonSetting = commonSettingServicePrx.getCommonSetting(user.getCompanyId(), "approve", "pauseLeaveLimit");
            if (!"1".equals(commonSetting.getContent())) {
                logger.info("设置不允许销假");
                throw new RpcServerLogicalException(1, "不允许提前结束请假");
            }
        } catch (RpcServerLogicalException e) {
            throw e;
        } catch (Throwable throwable) {
            // 2020年5月21日 12:43:11
            logger.error(throwable.getMessage(), throwable);
            EmailUtils.sendMail("获取销假设置出错", throwable);
        }
        leave.setIsLeavePause(1);
        leave.setPauseBy(user.getContactId());
        leave.setPauseTime(pauseLeaveTime);
        leave.setRealEndTime(pauseLeaveTime);
        if (leave.getRealStartTime().after(pauseLeaveTime)) {
            leave.setDuration(0);
            leave.setState(2);

            request.setRequestContentLastTotalSecond("0");
            request.setNaturalContentTimeLast("0小时");
            request.setManualEndingTime(new Timestamp(leave.getRealStartTime().getTime()));
            if (request.getDefaultType() == Template.DAYS_OFF) {
                LeaveInfo leaveInfo = getLeaveInfo(request, leave);
                leave.setShowTime("0小时");
                leaveInfo.setShowTime("0小时");
                leaveInfo.setTotalTime("0小时");
                leaveInfo.setLeaveTimestamp(0);
                leave.setLeaveTimeDetail(JSONObject.toJSONString(leaveInfo));
            }
        } else {
            LeaveInfo leaveInfo = getLeaveInfo(request, leave);

            leave.setState(1);
            if (Utils.isNotNull(leaveInfo.getRealEndTime())) {
                leave.setRealEndTime(leaveInfo.getRealEndTime());
            }
            leave.setDuration(leaveInfo.getLeaveTimestamp());

            if (Objects.equals(leaveInfo.getWithoutWorkTime(), true)) {
                request.setRequestContentLastTotalSecond("0");
                request.setNaturalContentTimeLast("0小时");
                leave.setDuration(0);
                if (request.getDefaultType() == Template.DAYS_OFF) {
                    leave.setShowTime("0小时");
                    leaveInfo.setShowTime("0小时");
                    leaveInfo.setTotalTime("0小时");
                    leaveInfo.setLeaveTimestamp(0);
                }
            } else {
                request.setRequestContentLastTotalSecond(String.valueOf(leaveInfo.getLeaveTimestamp()));
                request.setNaturalContentTimeLast(leaveInfo.getShowTime());
            }
            request.setManualEndingTime(new Timestamp(pauseLeaveTime.getTime()));
            if (request.getDefaultType() == Template.DAYS_OFF) {
                leave.setLeaveTimeDetail(JSONObject.toJSONString(leaveInfo));
            }
        }
        request.setLeave(leave);
    }

    private LeaveInfo getLeaveInfo(Request request, Leave leave) throws Throwable {
        LeaveInfo param = new LeaveInfo();
        param.setLeaveStartTime(DateTimeHelper.format(leave.getRealStartTime(), DateTimeHelper.YEAR_MONTH_DAY_HOUR_MINUTE_PATTERN));
        param.setLeaveEndTime(DateTimeHelper.format(leave.getRealEndTime(), DateTimeHelper.YEAR_MONTH_DAY_HOUR_MINUTE_PATTERN));
        param.setCompanyId(request.getCompanyId());
        param.setContactId(request.getApplicant());
        param.setLeaveCalculateType(leave.getLeaveCalculateType());
        param.setLeaveTimeUnit(leave.getLeaveTimeUnit());
        param.setIgnoreExistLeave(true);
        CheckInDataServicePrx checkInDataServicePrx = ServantUtils.getObject(CheckInDataServicePrx.class);
        return checkInDataServicePrx.calculateLeaveTime(param);
    }

    private void checkPauseRequest(Request request, User user, List<Flow> flows) throws Throwable {
        if (Utils.isNull(request)) {
            throw new RpcServerLogicalException(1, "审批不存在");
        }
        if (request.getFinished() != Finished.AGREED && request.getFinished() != Finished.ARCHIVED) {
            throw new RpcServerLogicalException(1, "无法结束的审批状态");
        }
        if (Objects.equals(request.getApplicantCiid(), user.getCompanyInfoId())) {
            return;
        }
        if (FlowUtils.isExistUserFlow(flows, user)) {
            return;
        }
        UserServicePrx userServicePrx = ServantUtils.getObject(UserServicePrx.class);
        if (userServicePrx.isLeader(user.getContactId(), request.getApplicant())) {
            return;
        }
        throw new RpcServerLogicalException(1, "仅申请人及其上级有权结束请假");
    }

    @Override
    public void cancelPauseRequest(Request dto) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            RequestDao requestDao = new RequestDaoImpl(connection);
            LeaveDao leaveDao = new LeaveDaoImpl(connection);
            FlowDao flowDao = new FlowDaoImpl(connection);

            User user = dto.getUser();
            Request request = requestDao.selectRequestDetailById(dto.getRequestDkey());
            List<Flow> flows = flowDao.selectFlowsByRequestId(dto.getRequestDkey());

            checkCancelPauseRequest(user, request, flows);

            Leave firstLeave = leaveDao.selectFirstLeaveByRequestId(dto.getRequestDkey());
            Leave lastLeave = leaveDao.selectLastLeaveByRequestId(dto.getRequestDkey());

            request.setRequestContentLastTotalSecond(String.valueOf(firstLeave.getDuration()));
            request.setNaturalContentTimeLast(firstLeave.getShowTime());
            request.setManualEndingTime(null);

            DBUtils.begin(connection);
            leaveDao.deleteLeave(lastLeave);
            leaveDao.insertLeave(firstLeave);
            requestDao.updateRequestPauseLeaveInfo(request);
            DBUtils.commit(connection);

            RocketUtils.sendCancelPauseRequestMsg(user, request);

        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            DBUtils.rollback(connection);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }


    private void checkCancelPauseRequest(User user, Request request, List<Flow> flows) {
        if (Utils.isNull(request)) {
            throw new RpcServerLogicalException(1, "审批不存在");
        }
        if (request.getFinished() != Finished.AGREED && request.getFinished() != Finished.ARCHIVED) {
            throw new RpcServerLogicalException(1, "无法结束的审批状态");
        }
        if (Objects.equals(request.getApplicantCiid(), user.getCompanyInfoId())) {
            return;
        }
        if (FlowUtils.isExistUserFlow(flows, user)) {
            return;
        }
        throw new RpcServerLogicalException(1, "仅申请人及其上级有权撤销结束请假");
    }

    @Override
    public void addTempCc(Request dto) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            UserServicePrx userServicePrx = ServantUtils.getObject(UserServicePrx.class);
            CcDao ccDao = new CcDaoImpl(connection);
            RequestDao requestDao = new RequestDaoImpl(connection);
            FlowDao flowDao = new FlowDaoImpl(connection);

            Request request = requestDao.selectRequestDetailById(dto.getRequestDkey());
            List<Cc> ccList = ccDao.selectCcListByRequestId(dto.getRequestDkey());
            List<Flow> flows = flowDao.selectFlowsByRequestId(dto.getRequestDkey());
            User user = dto.getUser();

            checkAddTempCc(user, request, ccList, flows);

            List<User> userList = userServicePrx.getUserListByContactIds(dto.getCcTemp());

            List<Cc> ccListToAdd = new ArrayList<>();
            for (User ccUser : userList) {
                Cc cc = new Cc(request.getRequestDkey(), ccUser.getCompanyInfoId());
                if (ccList.contains(cc)) {
                    logger.info("抄送人{}已存在", ccUser.getCompanyInfoId());
                    continue;
                }
                cc.setCompanyId(user.getCompanyId());
                cc.setCcId(ccUser.getContactId());
                cc.setIsTemp(1);
                cc.setCreatorId(user.getCompanyInfoId());
                if (request.getFinished() == Finished.ARCHIVED || request.getFinished() == Finished.AGREED) {
                    cc.setFiledStatus(Cc.NEED_FILE);
                }
                ccList.add(cc);
                ccListToAdd.add(cc);
            }
            if (Utils.isEmpty(ccListToAdd)) {
                throw new RpcServerLogicalException(1, "抄送人已存在");
            }
            if (ccList.size() > 30) {
                throw new RpcServerLogicalException(1, "最多添加30个抄送人");
            }
            request.setCc(CcUtils.getCc(ccList));
            request.setCcCiid(CcUtils.getCcCiid(ccList));
            if (request.getFinished() == Finished.ARCHIVED) {
                // 已归档改成已批准
                request.setFinished(Finished.AGREED);
            }

            DBUtils.begin(connection);
            ccDao.insertBatchCc(ccListToAdd);
            requestDao.updateRequestCcInfo(request);
            DBUtils.commit(connection);

            RocketUtils.pushToRefresh(user, userList.stream().map(User::getContactId).collect(Collectors.toSet()));
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            DBUtils.rollback(connection);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }

    private void checkAddTempCc(User user, Request request, List<Cc> ccList, List<Flow> flows) throws Throwable {
        if (request.getFinished() > Finished.ARCHIVED || request.getFinished() < Finished.APPROVING) {
            throw new RpcServerLogicalException(1, "该审批当前状态无法添加抄送人");
        }
        if (UserUtils.isAdmin(user) || UserUtils.isVicePresident(user)) {
            return;
        }
        // 申请人
        if (Objects.equals(user.getCompanyInfoId(), request.getApplicantCiid())) {
            return;
        }
        // 审批人
        if (FlowUtils.isExistUserFlow(flows, user.getCompanyInfoId())) {
            return;
        }
        // 抄送人
        if (CcUtils.isExistUserCc(ccList, user.getCompanyInfoId())) {
            return;
        }
        // 申请人的上级
        UserServicePrx userServicePrx = ServantUtils.getObject(UserServicePrx.class);
        if (userServicePrx.isLeader(user.getContactId(), request.getApplicant())) {
            return;
        }
        throw new RpcServerLogicalException(1, "您无权添加临时抄送人");
    }

    @Override
    public void deleteTempCc(Request dto) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            UserServicePrx userServicePrx = ServantUtils.getObject(UserServicePrx.class);
            CcDao ccDao = DaoFactory.getCcDao(connection);
            RequestDao requestDao = new RequestDaoImpl(connection);
            RedDao redDao = DaoFactory.getRedDao(connection);

            User user = dto.getUser();
            User tempCcUser = userServicePrx.getUserByContactId(Integer.parseInt(dto.getCcTemp()));

            List<Cc> ccList = ccDao.selectCcListByRequestId(dto.getRequestDkey());
            Cc cc = CcUtils.getCcByCompanyInfoId(ccList, tempCcUser.getCompanyInfoId());

            checkDeleteTempCc(user, cc);

            Request request = requestDao.selectRequestDetailById(dto.getRequestDkey());

            // 移除抄送人
            ccList.remove(cc);
            // 重置审批流信息
            request.setCc(CcUtils.getCc(ccList));
            request.setCcCiid(CcUtils.getCcCiid(ccList));
            // 重置审批状态
            if (CcUtils.isAllFiled(ccList)) {
                request.setFinished(Finished.ARCHIVED);
            }

            DBUtils.begin(connection);
            requestDao.updateRequestCcInfo(request);
            ccDao.deleteCc(cc);
            redDao.deleteUserRedPoint(request.getRequestDkey(), tempCcUser.getCompanyInfoId());
            DBUtils.commit(connection);

            RocketUtils.pushToRefresh(tempCcUser);
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            DBUtils.rollback(connection);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }

    private void checkDeleteTempCc(User user, Cc cc) {
        if (Utils.isNull(cc)) {
            throw new RpcServerLogicalException(1, "抄送人不存在");
        }
        if (!Objects.equals(cc.getIsTemp(), 1)) {
            throw new RpcServerLogicalException(1, "默认抄送人不可删除");
        }
        if (cc.getFiledStatus() == Cc.HAD_FILED) {
            throw new RpcServerLogicalException(1, "该抄送人已将审批归档");
        }
        if (!Objects.equals(user.getCompanyInfoId(), cc.getCreatorId())) {
            throw new RpcServerLogicalException(1, "仅支持删除自己添加的抄送人");
        }
    }

    @Override
    public void switchTemplateTempCc(Integer ancestorId, Integer switchType) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            OperateApprovalDao operateApprovalDao = DaoFactory.getOperateApprovalDao(connection);

            DBUtils.begin(connection);

            operateApprovalDao.switchAllSubTemplateTempCc(ancestorId, switchType);

            DBUtils.commit(connection);
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            DBUtils.rollback(connection);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }

    @Override
    public void delComponentGroup(Integer companyId, Integer componentGroupId) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            OperateTemplateDao operateTemplateDao = DaoFactory.getOperateTemplateDao(connection);

            DBUtils.begin(connection);
            operateTemplateDao.deleteComponentGroup(companyId, componentGroupId);
            DBUtils.commit(connection);
        } catch (Throwable e) {
            DBUtils.rollback(connection);
            logger.error(e.getMessage(), e);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }

    @Override
    public Integer addComponentGroup(ComponentGroupDTO componentGroup, Integer companyId) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            OperateTemplateDao operateTemplateDao = DaoFactory.getOperateTemplateDao(connection);
//            ModifyDao modifyDao = new ModifyDaoImpl(connection);

//            ApprovalModify modify = new ApprovalModify();


            DBUtils.begin(connection);
            int groupId = operateTemplateDao.insertComponentGroup(componentGroup, companyId);
            DBUtils.commit(connection);

            return groupId == 0 ? null : groupId;
        } catch (Throwable e) {
            DBUtils.rollback(connection);
            logger.error(e.getMessage(), e);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }

    @Override
    public void initDefaultTemplate(Integer companyId) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            TemplateDao templateDao = new TemplateDaoImpl(connection);
            SelectTemplateDao selectTemplateDao = DaoFactory.getSelectTemplateDao(connection);
            KeyGeneratorServicePrx keyGeneratorService = ServantUtils.getObject(KeyGeneratorServicePrx.class);

            List<Integer> templateIds = selectTemplateDao.getAllTemplateIdList(companyId);
            if (Utils.isNotEmpty(templateIds)) {
                throw new RpcServerLogicalException(1, "该公司已经初始化默认模板");
            }

            List<Template> defaultTemplates = templateDao.getDefaultTemplateToInit();
            List<Integer> templateIdList = keyGeneratorService.generateKeys("requestTemplate", defaultTemplates.size());
            for (int i = 0; i < defaultTemplates.size(); i++) {
                Template defaultTemplate = defaultTemplates.get(i);
                defaultTemplate.setTemplateId(templateIdList.get(i));
            }
            DBUtils.begin(connection);
            templateDao.initMyCompanyTemplateList(companyId, defaultTemplates);
            DBUtils.commit(connection);
        } catch (Throwable e) {
            DBUtils.rollback(connection);
            logger.error(e.getMessage(), e);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }

    @Override
    public void removeCcRedpoint(User user, Integer type) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            CcDao ccDao = DaoFactory.getCcDao(connection);
            RedDao redDao = DaoFactory.getRedDao(connection);

            // 所有的红点
            List<Integer> redPointRequestIds = redDao.getAllRedPointRequestIds(user.getCompanyInfoId());

            DBUtils.begin(connection);

            if (type == 1) {
                if (ListHelper.isNotNullEmpty(redPointRequestIds)) {
                    // 筛选出我已归档且我有红点的审批数,然后消除这些红点
                    List<Integer> archivedHasRedPointRequestIds = ccDao.getMyArchivedHasRedPointRequestIds(ListHelper.list2string(redPointRequestIds), user.getCompanyInfoId());
                    if (ListHelper.isNotNullEmpty(archivedHasRedPointRequestIds)) {
                        redDao.clearRedPointByRequestIds(ListHelper.list2string(archivedHasRedPointRequestIds), user.getCompanyInfoId());
                    }
                }

            } else if (type == 0) {
                if (ListHelper.isNotNullEmpty(redPointRequestIds)) {
                    // 筛选出我已归档且我有红点的审批数,然后消除这些红点
                    List<Integer> archivedHasRedPointRequestIds = ccDao.getMyArchivedHasRedPointRequestIds(ListHelper.list2string(redPointRequestIds), user.getCompanyInfoId());
                    if (ListHelper.isNotNullEmpty(archivedHasRedPointRequestIds)) {
                        redDao.clearRedPointByRequestIds(ListHelper.list2string(archivedHasRedPointRequestIds), user.getCompanyInfoId());
                    }

                    // 待我归档审批
                    List<Integer> needToFileRequestIds = ccDao.getNeedToFileRequestIds(user.getCompanyId(), user.getCompanyInfoId());
                    if (ListHelper.isNotNullEmpty(needToFileRequestIds)) {
                        redDao.clearRedPointByRequestIds(ListHelper.list2string(needToFileRequestIds), user.getCompanyInfoId());
                    }
                }

                ccDao.removeUnFileAndUnReadRequests(user.getCompanyInfoId());
            }
            DBUtils.commit(connection);

            RocketUtils.pushToRefresh(user);
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            DBUtils.rollback(connection);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }

    @Override
    public void removeApproverRedpoint(User user, Integer type) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            RedDao redDao = DaoFactory.getRedDao(connection);
            FlowDao flowDao = new FlowDaoImpl(connection);
            List<Integer> redPoints = redDao.getAllRedPointRequestIds(user.getCompanyInfoId());

            if (Utils.isNotEmpty(redPoints)) {
                DBUtils.begin(connection);
                // 待我处理的有红点
                if (type == 0) {
                    removeNeedApprovedRedPoints(user, redDao, flowDao, redPoints);
                } else if (type == 1) {
                    // 我已处理有红点的审批
                    removeApprovedRedPoints(user, redDao, flowDao, redPoints);
                } else if (type == 2) {
                    removeNeedApprovedRedPoints(user, redDao, flowDao, redPoints);
                    removeApprovedRedPoints(user, redDao, flowDao, redPoints);
                }
                DBUtils.commit(connection);
            }
            RocketUtils.pushToRefresh(user);
        } catch (Throwable e) {
            DBUtils.rollback(connection);
            logger.error(e.getMessage(), e);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }

    @Override
    public void updateLeave(Integer companyId, List<String> dateList) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            LeaveDao leaveDao = new LeaveDaoImpl(connection);
            RequestDao requestDao = new RequestDaoImpl(connection);
            ComponentDao componentDao = new ComponentImpl(connection);

            dateList.sort((String::compareTo));
            String startTime = Utils.getFirst(dateList) + " 00:00:00";
            String endTime = Utils.getLast(dateList) + "23:59:59";
            List<Leave> leaves = leaveDao.selectCompanyLeaveDetailsByDateScope(companyId, startTime, endTime);
            if (Utils.isEmpty(leaves)) {
                return;
            }
            List<Integer> requestIds = LeaveUtils.getRequestIds(leaves);
            List<Request> leaveRequests = requestDao.selectRequestByIds(Utils.list2string(requestIds));
            Map<Integer, Request> requestMap = RequestUtils.getRequestMap(leaveRequests);
            List<Request> requestToUpdate = new ArrayList<>();
            List<Leave> leavesToUpdate = new ArrayList<>();
            List<TemplateComponent> componentToUpdate = new ArrayList<>();

            List<Integer> daysOffRequestIds = new ArrayList<>();
            Set<Integer> daysOffCompanyInfoIds = new HashSet<>();
            for (Leave leave : leaves) {
                if (isNotContains(leave.getRealStartTime(), leave.getRealEndTime(), dateList)) {
                    continue;
                }
                Request request = requestMap.get(leave.getRequestId());
                LeaveInfo leaveInfo = getLeaveInfo(request, leave);

                if (Objects.equals(leaveInfo.getWithoutWorkTime(), true)) {
                    leaveInfo.setContentLast(new ArrayList<>());
                    leaveInfo.setShowTime("0小时");
                    leaveInfo.setTotalTime("0小时");
                    leaveInfo.setLeaveTimestamp(0);
                    leaveInfo.setTimeInfos(new ArrayList<>());
                }
                request.setRequestContentLastTotalSecond(String.valueOf(leaveInfo.getLeaveTimestamp()));
                request.setNaturalContentTimeLast(leaveInfo.getShowTime());

                leave.setDuration(leaveInfo.getLeaveTimestamp());
                leave.setShowTime(leaveInfo.getShowTime());

                if (request.getDefaultType() == Template.DAYS_OFF) {
                    leave.setLeaveTimeDetail(JSONObject.toJSONString(leaveInfo));
                    daysOffRequestIds.add(leave.getRequestId());
                } else {
                    SimplePropertyPreFilter filter = new SimplePropertyPreFilter(LeaveInfo.class, "timeInfos", "showTime", "totalTime");
                    String leaveTimeDetail = JSONObject.toJSONString(leaveInfo, filter);
                    leave.setLeaveTimeDetail(leaveTimeDetail);
                }

                requestToUpdate.add(request);
                leavesToUpdate.add(leave);
            }

            if (Utils.isNotEmpty(daysOffRequestIds)) {
                Map<Integer, List<TemplateComponent>> requestComponentMap = componentDao.selectComponentMapByRequestIds(Utils.list2string(daysOffRequestIds));
                for (Leave leave : leavesToUpdate) {
                    if (!requestComponentMap.containsKey(leave.getRequestId())) {
                        continue;
                    }
                    Map<Integer, TemplateComponent> componentMap = ComponentUtils.getComponentMap(requestComponentMap.get(leave.getRequestId()));
                    TemplateComponent component = componentMap.get(4);
                    if (Utils.isNull(component)) {
                        logger.info("调休组件异常:{}", leave.getRequestId());
                        continue;
                    }
                    component.setValue(leave.getLeaveTimeDetail());
                    componentToUpdate.add(component);
                    daysOffCompanyInfoIds.add(leave.getCompanyInfoId());
                }

            }

            DBUtils.begin(connection);
            if (Utils.isNotEmpty(requestToUpdate)) {
                requestDao.updateBatchRequestLeaveInfo(requestToUpdate);
            }
            if (Utils.isNotEmpty(leavesToUpdate)) {
                leaveDao.deleteBatchLeave(leavesToUpdate);
                leaveDao.insertBatchLeave(leavesToUpdate);
            }
            if (Utils.isNotEmpty(componentToUpdate)) {
                componentDao.updateBatchComponentValue(componentToUpdate);
            }

            DBUtils.commit(connection);
            if (Utils.isNotEmpty(daysOffCompanyInfoIds)) {
                for (Integer companyInfoId : daysOffCompanyInfoIds) {
                    RocketUtils.sendHrCalculateDaysOff(companyId, companyInfoId);
                }
            }
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }

    private boolean isNotContains(Date start, Date end, List<String> dateList) {
        String startTime = DateTimeHelper.format(start);
        String endTime = DateTimeHelper.format(end);
        return dateList.stream().noneMatch(date -> startTime.compareTo(date) <= 0 && endTime.compareTo(date) >= 0);
    }

    @Override
    public void exchangeGeneralRequestFlows(ExchangeTemplateDTO exchangeTemplateDTO) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            SelectApprovalDao selectApprovalDao = DaoFactory.getSelectApprovalDao(connection);
            OperateTemplateDao operateTemplateDao = DaoFactory.getOperateTemplateDao(connection);

            Integer companyId = exchangeTemplateDTO.getCompanyId();
            Integer quitContactId = exchangeTemplateDTO.getQuitContactId();
            Integer receiveContactId = exchangeTemplateDTO.getReceiveContactId();
            String projectIds = exchangeTemplateDTO.getProjectIds();
            List<Integer> projectIdList = ListHelper.string2IntegerList(projectIds);

            List<GeneralRequestDTO> generalRequestDTOs = selectApprovalDao.getProjectGeneralRequestFlows(companyId, projectIdList);

            DBUtils.begin(connection);

            if (ListHelper.isNotNullEmpty(generalRequestDTOs)) {
                for (GeneralRequestDTO generalRequestDTO : generalRequestDTOs) {
                    if (isJudger(generalRequestDTO.getAllJudger(), quitContactId)) {
                        logger.info("审批流修改前:{}", generalRequestDTO.getAllJudger());
                        String newFlows = setNewFlows(quitContactId, receiveContactId, generalRequestDTO.getAllJudger());
                        logger.info("审批流修改后:{}", newFlows);
                        operateTemplateDao.changeGeneralRequestFlow(generalRequestDTO, companyId, newFlows);
                    }
                }

                String generalConditionFlowIds = getGeneralConditionFlowIds(generalRequestDTOs);
                List<Integer> GeneralConditionFlowIdList = getGeneralConditionFlowIdList(generalRequestDTOs);
                if (StringUtils.isNotEmpty(generalConditionFlowIds)) {
                    Map<Integer, List<GeneralConditionFlowDTO>> map = selectApprovalDao
                            .getGeneralConditionFlowDTOs(companyId, GeneralConditionFlowIdList);

                    for (String flowId : generalConditionFlowIds.split(",")) {
                        if (map.containsKey(Integer.valueOf(flowId))) {
                            List<GeneralConditionFlowDTO> newFlows =
                                    setNewFlowForGeneralConditionFlow(quitContactId, receiveContactId, map.get(Integer.valueOf(flowId)));
                            operateTemplateDao.changeGeneralConditionRequestFlow(newFlows, companyId);
                        }
                    }
                }
            }

            DBUtils.commit(connection);
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            DBUtils.rollback(connection);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }

    /**
     * 作废参与人
     *
     * @param requestId
     * @param user
     * @param connection
     */
    private void disableParticipants(Integer requestId, User user, Connection connection) throws Throwable {
        ParticipantDao participantDao = new ParticipantDaoImpl(connection);
        participantDao.updateParticipantState(requestId, user.getCompanyId());
    }

    /**
     * 作废审批流
     *
     * @param requestId
     * @param user
     * @param connection
     * @throws SQLException
     */
    private void disableFlow(Integer requestId, User user, Connection connection) throws Throwable {
        FlowDao flowDao = new FlowDaoImpl(connection);
        flowDao.updateFlowState(requestId, user.getCompanyId());
    }

    /**
     * 修改套件状态
     *
     * @param request
     * @param user
     * @param connection
     * @param templateType
     */
    private void initAndDisableSuite(Request request, User user, Connection connection, int templateType) {
        try {
            switch (templateType) {
                case Template.LEAVE:
                case Template.COMPENSATORY_LEAVE:
                case Template.DAYS_OFF:
                    // 请假套件
                    LeaveDao leaveDao = new LeaveDaoImpl(connection);
                    request.setLeave(leaveDao.selectLastLeaveByRequestId(request.getRequestDkey()));
                    leaveDao.deleteLeaveByRequestId(request.getRequestDkey(), user.getCompanyId());
                    break;
                case Template.OUTDOOR:
                case Template.BUSINESS_TRIP:
                    // 外勤套件
                    OutdoorDao outdoorDao = new OutdoorDaoImpl(connection);
                    request.setOutdoor(outdoorDao.selectLastOutdoorByRequestId(request.getRequestDkey()));
                    outdoorDao.deleteOutdoorByRequestId(request.getRequestDkey(), user.getCompanyId());
                    break;
                case Template.APPEND_SIGN:
                    // 补卡套件
                    AppendSignDao appendSignDao = new AppendSignDaoImpl(connection);
                    request.setAppendSigns(appendSignDao.selectAppendSignsByRequestId(request.getRequestDkey()));
                    appendSignDao.deleteAppendSignByRequestId(request.getRequestDkey(), user.getCompanyId());
                    break;
                case Template.WORK_OVERTIME:
                    // 加班套件
                    OvertimeDao overtimeDao = new OvertimeDaoImpl(connection);
                    overtimeDao.deleteOvertimeByRequestId(request.getRequestDkey(), user.getCompanyId());
                    request.setOvertimes(overtimeDao.selectOvertimesByRequestId(request.getRequestDkey()));
                    break;
                case Template.EXPENSES:
                case Template.PURCHASE:
                    // 报销、申购套件
                    ExpenseDao expenseDao = new ExpenseDaoImpl(connection);
                    expenseDao.deleteExpensesByRequestId(request.getRequestDkey(), user.getCompanyId());
                    break;
                case Template.OTHER:
                    MoneyDao moneyDao = new MoneyDaoImpl(connection);
                    moneyDao.updateMoneyState(request.getRequestDkey(), 2);
                    break;
                default:
                    break;
            }
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            EmailUtils.sendErrorMail("删除套件信息出错", e.getMessage());
        }
    }

    private void removeApprovedRedPoints(User user, RedDao redDao, FlowDao flowDao, List<Integer> redPoints) throws SQLException {
        List<Integer> requestIds = flowDao.selectRequestIdsApprovedByCompanyInfoIdWithRedPoint(user.getCompanyId(), user.getCompanyInfoId(), Utils.list2string(redPoints));
        if (Utils.isNotEmpty(requestIds)) {
            redDao.clearRedPointByRequestIds(ListHelper.list2string(requestIds), user.getCompanyInfoId());
        }
    }

    private void removeNeedApprovedRedPoints(User user, RedDao redDao, FlowDao flowDao, List<Integer> redPoints) throws SQLException {
        List<Integer> needApprovedIds = flowDao.selectNeedUserApproveIds(user.getCompanyId(), user.getCompanyInfoId());
        List<Integer> redPointRequestIds = ListHelper.getOverLapList(needApprovedIds, redPoints);
        if (Utils.isNotEmpty(redPointRequestIds)) {
            redDao.clearRedPointByRequestIds(ListHelper.list2string(redPointRequestIds), user.getCompanyInfoId());
        }
    }

    private List<GeneralConditionFlowDTO> setNewFlowForGeneralConditionFlow(Integer quitContactId,
                                                                            Integer receiveContactId, List<GeneralConditionFlowDTO> generalConditionFlowDTOS) {

        List<GeneralConditionFlowDTO> newFlows = new ArrayList<>();

        for (GeneralConditionFlowDTO conditionFlowDTO : generalConditionFlowDTOS) {
            boolean flag = false;
            List<String> newFlowIds = new ArrayList<>();
            for (String flow : conditionFlowDTO.getRequestFlow().split(",")) {
                if (flow.equals(String.valueOf(quitContactId))) {
                    newFlowIds.add(String.valueOf(receiveContactId));
                    flag = true;
                } else {
                    newFlowIds.add(flow);
                }
            }
            if (flag) {
                logger.info("条件审批流修改前:{}, ", conditionFlowDTO.getRequestFlow());
                conditionFlowDTO.setRequestFlow(ListHelper.list2string(newFlowIds));
                logger.info("条件审批流修改后:{}, ", conditionFlowDTO.getRequestFlow());
                newFlows.add(conditionFlowDTO);
            }
        }

        return newFlows;
    }

    private List<Integer> getGeneralConditionFlowIdList(List<GeneralRequestDTO> generalRequestDTOS) {
        List<Integer> generalConditionFlowIds = new ArrayList<>();

        for (GeneralRequestDTO generalRequestDTO : generalRequestDTOS) {
            if (generalRequestDTO.getAllJudger().equals(RequestFlowType.CONDITION)) {
                generalConditionFlowIds.add(generalRequestDTO.getId());
            }
        }

        return generalConditionFlowIds;
    }

    private String getGeneralConditionFlowIds(List<GeneralRequestDTO> generalRequestDTOS) {
        List<Integer> generalConditionFlowIds = new ArrayList<>();

        for (GeneralRequestDTO generalRequestDTO : generalRequestDTOS) {
            if (generalRequestDTO.getAllJudger().equals(RequestFlowType.CONDITION)) {
                generalConditionFlowIds.add(generalRequestDTO.getId());
            }
        }

        return ListHelper.list2string(generalConditionFlowIds);
    }

    private String setNewFlows(Integer quitContactId, Integer receiveContactId, String allJudger) {
        List<String> newFlows = new ArrayList<>();

        for (String flow : allJudger.split(",")) {
            if (flow.equals(String.valueOf(quitContactId))) {
                newFlows.add(String.valueOf(receiveContactId));
            } else {
                newFlows.add(flow);
            }
        }

        return ListHelper.list2string(newFlows);
    }

    private boolean isJudger(String allJudger, Integer quitContactId) {
        List<String> flows = ListHelper.string2NumStringList(allJudger);
        return flows.contains(String.valueOf(quitContactId));
    }

}
